package com.smy.bc.fabric.core;

import com.smy.bc.fabric.core.configuration.FabricConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.ProposalException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.regex.Pattern;

/**
 * <p>Title: FabricTemplate</p>
 * <p>Description: </p>
 *
 * @author zhaofeng
 * @version 1.0
 * @date 18-9-14
 */
@Slf4j
@Component
public class FabricTemplate {
    
    @Resource
    private HFClient client;

    @Resource
    private FabricConfiguration configuration;

    private  Pattern chainCodePattern = Pattern.compile(".*");
    private  Pattern eventPattern = Pattern.compile(Pattern.quote("create"));

    /***
     * invoke blockChain
     * @param funcName
     * @param args
     * @throws ProposalException
     * @throws InvalidArgumentException
     */
    public void invokeBlockChain(String funcName, String... args) throws ProposalException, InvalidArgumentException {
        Channel channel = client.getChannel(configuration.getChannelName());
        // 构建proposal
        TransactionProposalRequest req = client.newTransactionProposalRequest();
        // 指定要调用的chaincode
        ChaincodeID cid = ChaincodeID.newBuilder().setName(configuration.getName()).build();
        req.setChaincodeID(cid);
        req.setFcn(funcName);

        if (args.length > 0) {
            req.setArgs(args);
        }
        // 发送proprosal
        Collection<ProposalResponse> resps = channel.sendTransactionProposal(req);

        // 提交给orderer节点
        channel.sendTransaction(resps).whenComplete((e,f)->{log.info("{} 执行完成... {}",e.getTransactionID(),f.getMessage());});
    }

    /***
     * query blockChain
     * @param funcName
     * @param args
     * @throws ProposalException
     * @throws InvalidArgumentException
     */
    public Collection<ProposalResponse> queryBlockChain(String funcName, String... args) throws ProposalException, InvalidArgumentException {
        // get channel instance from client
        Channel channel = client.getChannel(configuration.getChannelName());
        // create chaincode request
        QueryByChaincodeRequest qpr = client.newQueryProposalRequest();
        // build cc id providing the chaincode name. Version is omitted here.
        ChaincodeID fabcarCCId = ChaincodeID.newBuilder().setName(configuration.getName()).build();
        qpr.setChaincodeID(fabcarCCId);
        // CC function to be called
        qpr.setFcn(funcName);

        if (args.length > 0) {
            qpr.setArgs(args);
        }
        Collection<ProposalResponse> res = channel.queryByChaincode(qpr);
        // display response
        for (ProposalResponse pres : res) {

            if(pres.isVerified()){
                String stringResponse = new String(pres.getChaincodeActionResponsePayload());
                log.info("transactionID:{} payLoad:{}",pres.getTransactionID(),stringResponse);
            }else {
                log.error("status:{} msg:{}",pres.getStatus(),pres.getMessage());
            }
        }
        return res;
    }


    public String registerChaincodeEventListener(){
        // get channel instance from client
        Channel channel = client.getChannel(configuration.getChannelName());
        String chaincodeEventListenerHandle = null;
        try {
            chaincodeEventListenerHandle = channel.registerChaincodeEventListener(chainCodePattern,
                                                                                  eventPattern,
                                                                                  (handle, blockEvent, chaincodeEvent) -> {


                                                                                             String es = blockEvent.getPeer() != null ? blockEvent.getPeer().getName() : blockEvent.getEventHub().getName();

                                                                                             log.info("RECEIVED Chaincode event with handle: {}, chaincode Id: {}, chaincode event name: {}, "
                                                                                                      + "transaction id: {}, event payload: \"{}\", from eventhub: {} , blockNumber:{}",
                                                                                                      handle, chaincodeEvent.getChaincodeId(),
                                                                                                      chaincodeEvent.getEventName(),
                                                                                                      chaincodeEvent.getTxId(),
                                                                                                      new String(chaincodeEvent.getPayload()), es,blockEvent.getBlockNumber());

                                                                                         });
        } catch (InvalidArgumentException e) {
            log.error("事件监听异常!",e);
        }
        return chaincodeEventListenerHandle;
    }

    public boolean unregisterChaincodeEventListener(String handle){
        Channel channel = client.getChannel(configuration.getChannelName());
        boolean b = false;
        try {
            b = channel.unregisterChaincodeEventListener(handle);
        } catch (InvalidArgumentException e) {
            log.error("解绑异常!",e);
        }

        return b;

    }

    public String registerBlockListener(){
        // get channel instance from client
        Channel channel = client.getChannel(configuration.getChannelName());
        String blockEventListenerHandle = null;
        try {
            blockEventListenerHandle = channel.registerBlockListener((blockEvent) -> {
                log.info("RECEIVED block number: {}, "
                         + "dataCount: {}, name \"{}\", from eventhub: {}",
                         blockEvent.getBlockNumber(), blockEvent.getBlock().getData().getDataCount(),blockEvent.getPeer().getName());
            });
        } catch (InvalidArgumentException e) {
            log.error("事件监听异常!",e);
        }
        return blockEventListenerHandle;
    }

    public boolean unregisterBlockListener(String handle){
        Channel channel = client.getChannel(configuration.getChannelName());
        boolean b = false;
        try {
            b = channel.unregisterBlockListener(handle);
        } catch (InvalidArgumentException e) {
            log.error("解绑异常!",e);
        }
        return b;

    }

}
